<template>
  <div class="scene-prc1">
    <!-- threejs官网的 ‘一些有用的链接’-场景图 -->
    <!-- 会动的坦克 -->
    <div class="canvas" ref="canvas"></div>
  </div>
</template>

<script lang="ts">
import { Vue, Options } from 'vue-property-decorator';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
const scene =  new THREE.Scene();
{
  // 平行光 DirectionalLight(color,光照强度)
  const light =   new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(0, 20, 0);
  scene.add(light);
  // castShadow设置为true 该平行光会产生动态阴影
  light.castShadow = true;
  // shadow对象用来计算该平行光产生的阴影
  // mapSize定义阴影贴图的宽度和高度 调节阴影的清晰度
  light.shadow.mapSize.width = 2048;
  light.shadow.mapSize.height = 2048;
  const d = 50;
  // camera调节阴影的大小 d值越大，阴影投射的范围越大 如果阴影看不到可能是camera的far问题 可以设置far大一些
  light.shadow.camera.left = -d;
  light.shadow.camera.right = d;
  light.shadow.camera.top = -d;
  light.shadow.camera.bottom = d;
  light.shadow.camera.near = 1;
  light.shadow.camera.far = 50;
  // bias此处非常小的调整（大约0.0001）可能有助于减少阴影中的伪影
  light.shadow.bias = 0.001;
}
{
  const light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(1, 2, 4);
  scene.add(light);
}
const renderer = new THREE.WebGLRenderer({
  alpha: true,
  antialias: true,
});
renderer.debug.checkShaderErrors = false;
renderer.setClearColor(new THREE.Color(), 0);
renderer.setPixelRatio(750 / window.innerWidth);
// 渲染器启用阴影
renderer.shadowMap.enabled  = true;
@Options({ components: {} })
export default class ScenePrc1 extends Vue {
  public rect: DOMRect|null = null;

  public resizeRendererToDisplaySize(renderer: THREE.WebGLRenderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return  needResize;
  }
  public makeCamera(fov = 40) {
    return new THREE.PerspectiveCamera(fov, window.innerWidth / window.innerHeight, 0.1, 1000);
  }
  public mounted() {
    this.rect = (this.$el as HTMLElement).getBoundingClientRect();
    renderer.setSize(this.rect.width, this.rect.height, false);
    (this.$refs.canvas as HTMLDivElement).append(renderer.domElement);
    // 此摄像机没有被添加进场景图
    const camera = this.makeCamera();
    // multiplyScalar将该向量与所传入的标量s进行相乘。
    camera.position.set(8, 4, 10).multiplyScalar(3);
    camera.lookAt(0, 0, 0);

    // 地板
    const groundGeometry =  new THREE.PlaneGeometry(50, 50);
    const groundMaterial = new THREE.MeshPhongMaterial({ color: 0xcc8866 });
    const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial);
    groundMesh.rotation.x = -Math.PI / 2;
    // receiveShadow材质是否接收阴影
    groundMesh.receiveShadow = true;
    scene.add(groundMesh);

    // 坦克整体
    const tank = new THREE.Object3D();
    scene.add(tank);

    // 车身
    const carWidth = 4;
    const carHeight = 1;
    const carLength = 8;
    const bodyGeometry = new THREE.BoxGeometry(carWidth, carHeight, carLength);
    const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x6688AA });
    const bodyMesh = new THREE.Mesh(bodyGeometry, bodyMaterial);
    bodyMesh.position.y = 1.4;
    // castShadow 是否产生动态阴影
    bodyMesh.castShadow = true;
    tank.add(bodyMesh);

    // 在坦克后上方的摄像头
    const tankCamera = this.makeCamera(75);
    tankCamera.position.y = 3;
    tankCamera.position.z = -6;
    tankCamera.rotation.y = Math.PI;
    bodyMesh.add(tankCamera);

    // 车轮
    const wheelHeight = 1;
    const wheelGeometry = new THREE.CylinderGeometry(1, 1, wheelHeight, 6);
    const wheelMaterial = new THREE.MeshPhongMaterial({ color: 0x888888 });
    const wheelPositions = [
      [-carWidth / 2 - wheelHeight / 2, -carHeight / 2, carLength / 3],
      [carWidth / 2 + wheelHeight / 2, -carHeight / 2, carLength / 3],
      [-carWidth / 2 - wheelHeight / 2, -carHeight / 2, 0],
      [carWidth / 2 + wheelHeight / 2, -carHeight / 2, 0],
      [-carWidth / 2 - wheelHeight / 2, -carHeight / 2, -carLength / 3],
      [carWidth / 2 + wheelHeight / 2, -carHeight / 2, -carLength / 3],
    ];
    // map() 方法创建一个新数组，这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成
    const wheelMeshes = wheelPositions.map((position) => {
      const mesh = new THREE.Mesh(wheelGeometry, wheelMaterial);
      // mesh.position.set(...position); 错误的？改写
      mesh.position.set(position[0], position[1], position[2]);
      mesh.rotation.z = Math.PI / 2;
      mesh.castShadow = true;
      bodyMesh.add(mesh);
      return mesh;
    });

    // 车厢
    const domeGeometry = new THREE.SphereGeometry(2, 12, 12, 0, Math.PI * 2, 0, Math.PI / 2);
    const domeMesh = new THREE.Mesh(domeGeometry, bodyMaterial);
    domeMesh.castShadow = true;
    bodyMesh.add(domeMesh);
    domeMesh.position.y = .5;

    // 坦克发射头
    const turretLength = carLength * .75 * .2;
    const turretGeometry = new THREE.BoxGeometry(.1, .1, turretLength);
    const turretMesh = new THREE.Mesh(turretGeometry, bodyMaterial);
    turretMesh.castShadow = true;
    const turretPivot  = new THREE.Object3D();
    turretPivot.scale.set(5, 5, 5);
    turretPivot.position.y = .5;
    turretMesh.position.z = turretLength * .5;
    turretPivot.add(turretMesh);
    bodyMesh.add(turretPivot);

    // 在坦克自身观察目标的摄像头
    const turretCamera = this.makeCamera();
    turretCamera.position.y = .75 * .2;
    turretMesh.add(turretCamera);

    // 发射目标
    const targetGeometry = new THREE.SphereGeometry(.5, 6, 3);
    const targetMaterial = new THREE.MeshPhongMaterial({
      color: 0x00ff00,
      flatShading: true,
    });
    const targetMesh = new THREE.Mesh(targetGeometry, targetMaterial);
    targetMesh.castShadow = true;
    const targetOrbit = new THREE.Object3D();
    const targetElevation = new THREE.Object3D();
    const targetBob = new THREE.Object3D();
    scene.add(targetOrbit);
    targetOrbit.add(targetElevation);
    targetElevation.position.z = carLength * 2;
    targetElevation.position.y = 8;
    targetElevation.add(targetBob);
    targetBob.add(targetMesh);

    // 在发射目标观察坦克的摄像机
    const targetCamera = this.makeCamera();
    const targetCameraPivot = new THREE.Object3D();
    targetCamera.position.y = 1;
    targetCamera.position.z = -2;
    targetCamera.rotation.y = Math.PI;
    targetBob.add(targetCameraPivot);
    targetCameraPivot.add(targetCamera);

    // 创建一个样条曲线
    const curve = new THREE.SplineCurve([
      new THREE.Vector2(-10, 0),
      new THREE.Vector2(-5, 5),
      new THREE.Vector2(0, 0),
      new THREE.Vector2(5, -5),
      new THREE.Vector2(10, 0),
      new THREE.Vector2(5, 10),
      new THREE.Vector2(-5, 10),
      new THREE.Vector2(-10, -10),
      new THREE.Vector2(-15, -8),
      new THREE.Vector2(-10, 0),
    ]);
    // getPoints 50要将曲线划分为的分段数 返回的是一个数组
    const points = curve.getPoints(50);
    // console.log(points);
    // setFromPoints通过点队列设置该 BufferGeometry 的 attribute。
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const material = new  THREE.LineBasicMaterial({ color: 0xff0000 });
    const splineObject = new THREE.Line(geometry, material);
    splineObject.rotation.x  = Math.PI / 2;
    splineObject.position.y = 0.05;
    scene.add(splineObject);

    const targetPosition = new THREE.Vector3();
    const tankPosition = new THREE.Vector2();
    const tankTarget = new THREE.Vector2();

    const cameras = [
      { cam: camera, desc: 'detached camera' },
      { cam: turretCamera, desc: 'on turret looking at target' },
      { cam: targetCamera, desc: 'near target looking at tank' },
      { cam: tankCamera, desc: 'above back of tank' },
    ];

    renderer.setAnimationLoop((time) => {
      // eslint-disable-next-line no-param-reassign
      time *= 0.001;
      if (this.resizeRendererToDisplaySize(renderer)) {
        const canvas = renderer.domElement;
        cameras.forEach((cameraInfo) => {
          const camera = cameraInfo.cam;
          camera.aspect = canvas.clientWidth / canvas.clientHeight;
          camera.updateProjectionMatrix();
        });
      }

      // 移动要打击的目标
      targetOrbit.rotation.y =   time * .27;
      targetBob.position.y = Math.sin(time * 2) * 4;
      targetMesh.rotation.x = time * 7;
      targetMesh.rotation.y = time * 13;
      // eslint-disable-next-line no-mixed-operators
      targetMaterial.emissive.setHSL(time * 10 % 1, 1, .25);
      // eslint-disable-next-line no-mixed-operators
      targetMaterial.color.setHSL(time * 10 % 1, 1, .25);

      // 移动坦克
      const tankTime = time * .05;
      // 根据弧长返回曲线上给定位置的点。
      curve.getPointAt(tankTime % 1, tankPosition);
      curve.getPointAt((tankTime + 0.01) % 1, tankTarget);
      tank.position.set(tankPosition.x, 0, tankPosition.y);
      tank.lookAt(tankTarget.x, 0, tankTarget.y);

      // 通过getWorldPosition方法，获取targetMesh的世界坐标结果到targetPosition中
      targetMesh.getWorldPosition(targetPosition);
      turretPivot.lookAt(targetPosition);

      turretCamera.lookAt(targetPosition);

      tank.getWorldPosition(targetPosition);
      targetCameraPivot.lookAt(targetPosition);

      wheelMeshes.forEach((obj) => {
        obj.rotation.x = time * 3;
      });

      // eslint-disable-next-line no-mixed-operators
      // const camera = cameras[time * .25 % cameras.length | 0];
      const camera = cameras[0];
      renderer.render(scene, camera.cam);
    });

    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableZoom = false;
  }
}
</script>

<style lang="postcss" scoped>
  .scene-prc1{
    touch-action: none;
    position: absolute;
    left: 0;top: 0;
    width: 100%;height: 100%;
    background: #98bbd1;
    .canvas{
      position: absolute;
      left: 0;top: 0;
      width: 100%;height: 100%;
      :deep(canvas){
        width: 100%;height: 100%;
      }
    }
  }
</style>
